extern "C" {
    #include "AudioDecoder.h"
}
#include <android/log.h>
#include <jni.h>

#undef	LOG_TAG
#define LOG_TAG "AudioDecoder"
#define LOGI(...) __android_log_print (ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print (ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

typedef struct AudioCodecFFmpeg {
    AVCodecContext* pCodecCtx = nullptr;
    struct SwrContext* au_convert_ctx = nullptr;
    uint8_t *buffer = nullptr;
    jmethodID callbackMethod = nullptr;
} AudioCodecFFmpeg;

/**
 * 创建解码器
 *
 * @param codecid 解码器类型
 * @param sample_rate 采样率
 * @param channels 通道数
 * @param bit_rate 采样码率
 * @return
 */
void* audio_decoder_create(AVCodecID codecid, int sample_rate, int channels, int bit_rate)
{
    auto *pAudioCodec = (AudioCodecFFmpeg *)malloc(sizeof(AudioCodecFFmpeg));
    AVCodec *pCodec = avcodec_find_decoder(codecid);//AV_CODEC_ID_AAC
    if (pCodec == nullptr)
    {
        LOGD("find %d decoder error", codecid);
        return nullptr;
    }
    LOGD("audio_decoder_create codecid=%d", codecid);

    pAudioCodec->pCodecCtx = avcodec_alloc_context3(pCodec);
    pAudioCodec->pCodecCtx->channels = channels;
    pAudioCodec->pCodecCtx->sample_rate = sample_rate;
    pAudioCodec->pCodecCtx->bit_rate = bit_rate;
    if (codecid == AV_CODEC_ID_ADPCM_G726||codecid == AV_CODEC_ID_ADPCM_G726LE){
        //码字,G726需要设置该值，相当于ffmpeg命令编码及播放g726音频时的参数：code_size,该值一般情况下为比特率除以8k,范围为2~5取整
        pAudioCodec->pCodecCtx->bits_per_coded_sample = bit_rate/8000;
    }

    pAudioCodec->buffer = nullptr;
    pAudioCodec->callbackMethod = nullptr;

    if(avcodec_open2(pAudioCodec->pCodecCtx, pCodec, nullptr) < 0)
    {
        LOGD("open codec error\r\n");
        return nullptr;
    }

    //创建重采样的转换器
    pAudioCodec->au_convert_ctx = swr_alloc_set_opts(nullptr,
                                                     pAudioCodec->pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16, pAudioCodec->pCodecCtx->sample_rate,
                                                     pAudioCodec->pCodecCtx->channel_layout, pAudioCodec->pCodecCtx->sample_fmt, pAudioCodec->pCodecCtx->sample_rate,
                                                     0, nullptr);

    swr_init(pAudioCodec->au_convert_ctx);
    LOGD("audio_decoder_create end");
    return (void *)pAudioCodec;
}

/**
 * 解码数据，数据量建议小于等于一帧
 * @param env
 * @param instance
 * @param pParam 解码器
 * @param pData 待解码的数据
 * @param nLen 数据大小
 * @return
 */
int audio_decode_frame(JNIEnv *env, jobject instance,void *pParam, unsigned char *pData, int nLen)
{

    auto *pAudioCodec = (AudioCodecFFmpeg *)pParam;

    AVPacket* packet = av_packet_alloc();

    packet->data = pData;
    packet->size = nLen;

    //将数据发送给解码器
    int ret = avcodec_send_packet(pAudioCodec->pCodecCtx,packet);
    if (ret != 0){
        av_packet_free(&packet);
        av_free(packet);
        packet = nullptr;
        return -1;
    }

    AVFrame* avFrame = av_frame_alloc();
    ret = avcodec_receive_frame(pAudioCodec->pCodecCtx,avFrame);
    //从解码器中读取一帧，返回值为0时为读取成功

    if (ret == AVERROR(EAGAIN)||ret < 0){
        //AVERROR(EAGAIN):需要更多的输入数据才能解码

        av_packet_free(&packet);
        av_free(packet);
        packet = nullptr;

        av_frame_free(&(avFrame));
        av_free(avFrame);
        avFrame = nullptr;

        return -1;
    }

    //获取输出的一帧的PCM数据的大小
    int buffer_size = av_samples_get_buffer_size(nullptr, pAudioCodec->pCodecCtx->channels, avFrame->nb_samples, AV_SAMPLE_FMT_S16, 1);

    if (pAudioCodec->buffer == nullptr){
        //一般每一帧的大小都是一样的，除了最后一帧可能会小一些
        pAudioCodec->buffer = (uint8_t *) av_malloc(buffer_size);
    }

    int len = swr_convert(
            pAudioCodec->au_convert_ctx,
            &pAudioCodec->buffer,
            buffer_size,
            (const uint8_t **) (avFrame->data) ,
            avFrame->nb_samples);
    //第三个参数网上有人用avFrame->nb_samples，测试改后倒是也可以正常播放
    //但按理来说第三个参数应该是一帧的大小即buffer_size

    if (len > 0)
    {
        jbyteArray array = env->NewByteArray(buffer_size);

        //两行代码效果一样，但网上部分文章应用后者
        env->SetByteArrayRegion(array,0,buffer_size,(jbyte*)pAudioCodec->buffer);
//        env->SetByteArrayRegion(array,0, avFrame->linesize[0],(jbyte*)avFrame->data[0]);

        if (pAudioCodec->callbackMethod == NULL){
            jclass clazz = env->GetObjectClass(instance);
            pAudioCodec->callbackMethod = env->GetMethodID(clazz,"onFrame","([B)V");
        }

        env->CallVoidMethod(instance,pAudioCodec->callbackMethod,array);
    }

    av_packet_free(&packet);
    av_free(packet);
    packet = nullptr;

    av_frame_free(&(avFrame));
    av_free(avFrame);
    avFrame = nullptr;

    return 0;
}

/**
 * 释放解码器
 * @param pParam
 */
void audio_decoder_close(void *pParam)
{
    auto *pCodec = (AudioCodecFFmpeg *)pParam;
    if (pCodec == nullptr)
    {
        return;
    }

    swr_free(&pCodec->au_convert_ctx);


    if (pCodec->buffer != nullptr){
        av_free(pCodec->buffer);
    }

    if (pCodec->pCodecCtx != nullptr)
    {
        avcodec_close(pCodec->pCodecCtx);
        avcodec_free_context(&pCodec->pCodecCtx);
        pCodec->pCodecCtx = nullptr;
    }

    free(pCodec);
}
